﻿using System;
using System.Reflection;

namespace UnityEngine
{
    public static class MonoBehaviourEx
    {
        public static void BindUIChildren(this MonoBehaviour monoBehaviour)
        {
            Type monoType = monoBehaviour.GetType();
            Type transformExtType = typeof(TransformEx);
            MethodInfo findMethod = transformExtType.GetMethod("FindChild");

            FieldInfo[] fields = monoType.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
            for (int i = 0; i < fields.Length; i++)
            {
                object[] bindChildAttrs = fields[i].GetCustomAttributes(typeof(UIChildAttribute), false);
                if (bindChildAttrs.Length > 0)
                {
                    UIChildAttribute uiChildAttr = bindChildAttrs[0] as UIChildAttribute;
                    string childName = uiChildAttr.ChildName == "this" ? monoBehaviour.name : uiChildAttr.ChildName;
                    object child = findMethod.MakeGenericMethod(fields[i].FieldType).Invoke(null, new object[] { monoBehaviour.transform, childName });
                    if (child == null && uiChildAttr.AttachComponentType != null)
                    {
                        child = findMethod.MakeGenericMethod(typeof(Transform)).Invoke(null, new object[] { monoBehaviour.transform, childName });
                        if (child != null)
                        {
                            child = ((Component)child).gameObject.AddComponent(uiChildAttr.AttachComponentType);
                        }
                    }

                    if (child != null)
                    {
                        fields[i].SetValue(monoBehaviour, child);
                        ((Component)child).gameObject.SetActive(uiChildAttr.IsDefaultActive);
                    }
                    else
                    {
                        throw new BindFaildException("绑定子物体失败,类型:" + fields[i].FieldType + "与名称:" + uiChildAttr.ChildName + "不匹配");
                    }
                }
            }

            PropertyInfo[] properties = monoType.GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
            for (int i = 0; i < properties.Length; i++)
            {
                object[] bindChildAttrs = properties[i].GetCustomAttributes(typeof(UIChildAttribute), false);
                if (bindChildAttrs.Length > 0)
                {
                    UIChildAttribute uiChildAttr = bindChildAttrs[0] as UIChildAttribute;
                    string childName = uiChildAttr.ChildName == "this" ? monoBehaviour.name : uiChildAttr.ChildName;
                    object child = findMethod.MakeGenericMethod(properties[i].PropertyType).Invoke(null, new object[] { monoBehaviour.transform, childName });
                    if (child == null && uiChildAttr.AttachComponentType != null)
                    {
                        child = findMethod.MakeGenericMethod(typeof(Transform)).Invoke(null, new object[] { monoBehaviour.transform, childName });
                        if (child != null)
                        {
                            child = ((Component)child).gameObject.AddComponent(uiChildAttr.AttachComponentType);
                        }
                    }
                    if (child != null)
                    {
                        properties[i].SetValue(monoBehaviour, child, null);
                    }
                    else
                    {
                        throw new BindFaildException("绑定子物体失败,类型:" + properties[i].PropertyType + "与名称:" + uiChildAttr.ChildName + "不匹配");
                    }
                }
            }
        }

        /// <summary>
        /// 绑定子物体失败异常
        /// </summary>
        [Serializable]
        public class BindFaildException : Exception
        {
            public BindFaildException() { }
            public BindFaildException(string message) : base(message) { }
            public BindFaildException(string message, Exception inner) : base(message, inner) { }
            protected BindFaildException(
              System.Runtime.Serialization.SerializationInfo info,
              System.Runtime.Serialization.StreamingContext context) : base(info, context)
            { }
        }
    }

    /// <summary>
    /// 绑定子物体特性,在需要的字段上添加该特性后再调用Monobehaviour.BindChildren可以自动绑定所有子物体
    /// </summary>
    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
    public sealed class UIChildAttribute : Attribute
    {
        public string ChildName { get; private set; }
        public bool IsDefaultActive { get; private set; }
        public Type AttachComponentType { get; private set; }

        public UIChildAttribute(string childName, bool isDefaultActive, Type attachComponentType)
        {
            ChildName = childName;
            IsDefaultActive = isDefaultActive;
            AttachComponentType = attachComponentType;
        }

        public UIChildAttribute(string childName) : this(childName, true, null)
        {
        }

        public UIChildAttribute(string childName, bool isDefaultActive) : this(childName, isDefaultActive, null)
        {

        }

        public UIChildAttribute(string childName, Type attachComponentType) : this(childName, true, attachComponentType)
        {

        }
    }
}
